/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: json_value.cpp
 *
 * Purpose: json value implementation
 *
 * Developer:
 *   wen.gu , 2019-07-05
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "collie/core/json.h"


#include <array>
#include <exception>
#include <assert.h>
#include <cmath>
#include <string.h>


namespace collie
{
namespace core
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

#define VALUE_ASSERT_UNREACHABLE assert(false)

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/
using ObjectValue = Value::ObjectMembers;
using ArrayValue  = Value::ArrayElements;

#if 0
typedef union ValueHolder 
{
    int64_t mInt;
    uint64_t mUint;
    double mReal;
    bool mBool;
    char* mStr; 
    ObjectValueMap* mMap;
}ValueHolder;
#endif


// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static const double gMaxUInt64AsDouble = 18446744073709551615.0;

/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
template <typename T>
static std::unique_ptr<T> cloneUnique(const std::unique_ptr<T>& p) {
    std::unique_ptr<T> r;
    if (p) {
        r = std::unique_ptr<T>(new T(*p));
    }
    return r;
}

/******************************************************************************
 **   inner implementation FUNCTION DEFINITIONS
 ******************************************************************************/

class Value::impl
{
public:
    impl(ValueType vt)
    {
        allocVal(vt);
    }
    ~impl()
    {
        releaseVal();
    }

public:

    impl& operator=(const impl& other);
    impl& operator=(impl&& other) noexcept;

    impl& operator=(bool val);
    impl& operator=(int64_t val);
    impl& operator=(uint64_t val);
    impl& operator=(double val);
    impl& operator=(const std::string& val);
    impl& operator=(const char* val);
    impl& operator=(const ArrayValue& av);
    impl& operator=(const ObjectValue& ov);

public:
    bool operator==(const impl& other);
public:
    inline bool asBool() { return *(bool*)mVal; }
    inline int64_t asInt() { return *(int64_t*)mVal; }
    inline uint64_t asUint() { return *(uint64_t*)mVal; }
    inline double asReal() { return *(double*)mVal; }
    inline const char* asStr() { return (const char*)mVal; }
    inline ArrayValue& asArray() { return *(ArrayValue*)mVal; }
    inline ObjectValue& asObject() { return *(ObjectValue*)mVal; }  
    inline ValueType type() { return mType; }
private:
    void releaseVal();
    void allocVal(ValueType type);
    void setStr(const char* str, uint32_t len);
private:
    ValueType mType = ValueType::Null;
    uint8_t* mVal = nullptr;
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

Value::impl& Value::impl::operator=(const impl& other)
{
    switch (other.mType)
    {
    case ValueType::Boolean:
        *this = *(bool*)other.mVal;
        break;
    case ValueType::Int:
        *this = *(int64_t*)other.mVal;
        break;
    case ValueType::Uint:
        *this = *(uint64_t*)other.mVal;
        break;
    case ValueType::Real:
        *this = *(double*)other.mVal;
        break;
    case ValueType::String:
        *this = (const char*)other.mVal;
        break;
    case ValueType::Object:
        *this = *(ObjectValue*)other.mVal;
        break;
    case ValueType::Array:
        *this = *(ArrayValue*)other.mVal;
        break;
    case ValueType::Null:
    default:
        releaseVal();
        allocVal(other.mType);
        break;
    }

    return *this;
}

Value::impl& Value::impl::operator=(impl&& other) noexcept
{
    releaseVal();
    mType = other.mType;
    mVal = other.mVal;
    other.mVal = nullptr;

    return *this;
}

Value::impl& Value::impl::operator=(bool val)
{
    if (mType != ValueType::Boolean)
    {
        releaseVal();
        allocVal(ValueType::Boolean);
    }

    *(bool*)mVal = val;
    return *this;
}

Value::impl& Value::impl::operator=(int64_t val)
{
    if (mType != ValueType::Int)
    {
        releaseVal();
        allocVal(ValueType::Int);
    }

    *(int64_t*)mVal = val;
    return *this;
}

Value::impl& Value::impl::operator=(uint64_t val)
{
    if (mType != ValueType::Uint)
    {
        releaseVal();
        allocVal(ValueType::Uint);
    }

    *(uint64_t*)mVal = val;
    return *this;
}

Value::impl& Value::impl::operator=(double val)
{
    if (mType != ValueType::Real)
    {
        releaseVal();
        allocVal(ValueType::Real);
    }

    *(double*)mVal = val;
    return *this;
}

Value::impl& Value::impl::operator=(const std::string& val)
{
    setStr(val.c_str(), uint32_t(val.size()));
    return *this;
}

Value::impl& Value::impl::operator=(const char* val)
{
    setStr(val, uint32_t(strlen(val)));
    return *this;
}

Value::impl& Value::impl::operator=(const ArrayValue& val)
{
    if (mType != ValueType::Array)
    {
        releaseVal();
        allocVal(ValueType::Array);
    }

    *(ArrayValue*)mVal = val;
    return *this;
}

Value::impl& Value::impl::operator=(const ObjectValue& val)
{
    if (mType != ValueType::Object)
    {
        releaseVal();
        allocVal(ValueType::Object);
    }

    *(ObjectValue*)mVal = val;
    return *this;
}



void Value::impl::releaseVal()
{
    if (mVal)
    {
        if (mType == ValueType::Object)
        {
            delete (ObjectValue*)mVal;
        }
        else if (mType == ValueType::Array)
        {
            delete (ArrayValue*)mVal;
        }
        else
        {
            delete[] mVal;
        }

        mVal = nullptr;
    }
}

void Value::impl::allocVal(ValueType type)
{
    mType = type;
    switch (type)
    {
    case ValueType::Object:
        mVal = (uint8_t*)new ObjectValue;
        break;
    case ValueType::Array:
        mVal = (uint8_t*)new ArrayValue;
        break;
    case ValueType::String:
    case ValueType::Null:
        /** do nothing for those type*/
        break;
    default:  /** for others */
        mVal = new uint8_t[sizeof(uint64_t)]; /**todo whice type is bigger? */
        break;
    }
}

void Value::impl::setStr(const char* str, uint32_t len)
{
    releaseVal();
    allocVal(ValueType::String);
    mVal = new uint8_t[len + 1];
    memcpy(mVal, str, len);
    mVal[len] = '\0';
}

bool Value::impl::operator==(const impl& other)
{
    bool ret = false;
    if (mType == other.mType)
    {
        if (mType != ValueType::Null)
        {
            if ((mVal != nullptr) && (other.mVal != nullptr))
            {
                switch (mType)
                {
                case ValueType::Boolean:
                    ret = *(bool*)mVal == *(bool*)other.mVal;
                    break;
                case ValueType::Int:
                    ret = *(int64_t*)mVal == *(int64_t*)other.mVal;
                    break;
                case ValueType::Uint:
                    ret = *(uint64_t*)mVal == *(uint64_t*)other.mVal;
                    break;
                case ValueType::Real:
                    ret = *(double*)mVal == *(double*)other.mVal;
                    break;
                case ValueType::String:
                    ret = (strcmp((const char*)mVal, (const char*)other.mVal) == 0);
                    break;
                case ValueType::Array:
                {
                    ArrayValue& av = *(ArrayValue*)mVal;
                    ArrayValue& oav = *(ArrayValue*)other.mVal;
                    if ((av.size() == oav.size()) && (av == oav))
                    {
                        ret = true;
                    }
                    break;
                }
                case ValueType::Object:
                {
                    ObjectValue& ov = *(ObjectValue*)mVal;
                    ObjectValue& oov = *(ObjectValue*)other.mVal;

                    if ((ov.size() == oov.size()) && (ov == oov))
                    {
                        ret = true;
                    }
                    break;
                }
                default:
                    break;
                }
            }
        }
        else
        {/** if both null type, then needn't check mVal */
            ret = true;
        }
    }

    return ret;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

static inline bool IsIntegral(double d) 
{
    double integral_part;
    return modf(d, &integral_part) == 0.0;
}

static inline double integerToDouble(uint64_t value) 
{
    return static_cast<double>(int64_t(value / 2)) * 2.0 +
        static_cast<double>(int64_t(value & 1));
}

template <typename T> 
static inline double integerToDouble(T value) 
{
    return static_cast<double>(value);
}

template <typename T, typename U>
static inline bool InRange(double d, T min, U max) 
{
    return d >= integerToDouble(min) && d <= integerToDouble(max);
}


/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

const uint32_t Value::defaultRealPrecision = 17;


Value const& Value::nullSingleton() 
{
    static Value const nullStatic;
    return nullStatic;
}

// for backwards compatibility, we'll leave these global references around, but
// DO NOT use them in JSONCPP library code any more!
Value const& Value::null = Value::nullSingleton();
Value const& Value::nullRef = Value::nullSingleton();

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

const Value::LargestInt Value::minLargestInt = Value::LargestInt(~(Value::LargestUInt(-1) / 2));
/// Maximum signed integer value that can be stored in a Json::Value.
const Value::LargestInt Value::maxLargestInt = Value::LargestInt(Value::LargestUInt(-1) / 2);
/// Maximum unsigned integer value that can be stored in a Json::Value.
const Value::LargestUInt Value::maxLargestUInt = Value::LargestUInt(-1);

/// Minimum signed int value that can be stored in a Json::Value.
const Value::Int Value::minInt = Value::Int(~(Value::UInt(-1) / 2));
/// Maximum signed int value that can be stored in a Json::Value.
const Value::Int Value::maxInt = Value::Int(Value::UInt(-1) / 2);
/// Maximum unsigned int value that can be stored in a Json::Value.
const Value::UInt Value::maxUInt = Value::UInt(-1);

/// Minimum signed 64 bits int value that can be stored in a Json::Value.
const Value::Int64 Value::minInt64 = Value::Int64(~(Value::UInt64(-1) / 2));
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
const Value::Int64 Value::maxInt64 = Value::Int64(Value::UInt64(-1) / 2);
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
const Value::UInt64 Value::maxUInt64 = Value::UInt64(-1);


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

 /** constructors and destructor */
Value::Value(ValueType type /*= ValueType::Null*/) /** construct with value type */
    :mImpl(new impl(type))
{
    /** todo something */
}

Value::Value(bool val)
    :Value(ValueType::Boolean)
{
    *mImpl = val;
}

Value::Value(int32_t val)
    :Value(ValueType::Int)
{
    *mImpl = (int64_t)val;
}


Value::Value(uint32_t val)
    :Value(ValueType::Uint)
{
    *mImpl = (uint64_t)val;
}

Value::Value(int64_t val)
    :Value(ValueType::Int)
{
    *mImpl = val;
}

Value::Value(uint64_t val)
    :Value(ValueType::Uint)
{
    *mImpl = val;
}

Value::Value(double val)
    :Value(ValueType::Real)
{
    *mImpl = val;
}

Value::Value(const std::string& val)
    :Value(ValueType::String)
{
    *mImpl = val;
}

Value::Value(const Value& other)
    :Value(other.mImpl->type())
{
    *mImpl = *other.mImpl;
}

Value::Value(Value&& other) noexcept
    :Value(other.mImpl->type())
{
    *mImpl = std::move(*other.mImpl);
}

Value::~Value()
{
    /** todo something */
}


/** override assign value operator*/
Value& Value::operator=(const Value& other)
{
    *mImpl = *other.mImpl;
    return *this;
}

Value& Value::operator=(Value&& other) noexcept
{
    *mImpl = std::move(*other.mImpl);
    return *this;
}

Value& Value::operator=(bool val)
{
    *mImpl = val;
    return *this;
}

Value& Value::operator=(int32_t val)
{
    *mImpl = (int64_t)val;
    return *this;
}

Value& Value::operator=(uint32_t val)
{
    *mImpl = (uint64_t)val;
    return *this;
}

Value& Value::operator=(int64_t val)
{
    *mImpl = val;
    return *this;
}

Value& Value::operator=(uint64_t val)
{
    *mImpl = val;
    return *this;
}

Value& Value::operator=(double val)
{
    *mImpl = val;
    return *this;
}

Value& Value::operator=(const std::string& val)
{
    *mImpl = val;
    return *this;
}

/** for compare */
/* Compare payload only, not comments etc. */
bool Value::operator==(const Value& other) const
{
    return *mImpl == *other.mImpl;
}

bool Value::operator!=(const Value& other) const
{
    return !(*mImpl == *other.mImpl);
}


/** value type check */
bool Value::isNull() const
{
    return type() == ValueType::Null;
}

bool Value::isBool() const
{
    return type() == ValueType::Boolean;
}

bool Value::isInt() const
{
    switch (type()) 
    {
    case ValueType::Int:
        return  (mImpl->asInt() >= Value::minInt) && (mImpl->asInt() <= Value::maxInt);
    case ValueType::Uint:
        return mImpl->asUint() <= uint32_t(Value::maxInt);
    case ValueType::Real:
    {
        double rv = mImpl->asReal();
        return(rv >= Value::minInt) && ( rv <= Value::maxInt) && IsIntegral(rv);
    }
    default:
        break;
    }

    return false;
}

bool Value::isUInt() const
{
    switch (type()) {
    case ValueType::Int:
        return (mImpl->asInt() >= 0) && (uint64_t(mImpl->asInt()) <= uint64_t(UINT32_MAX)); 
    case ValueType::Uint:
        return mImpl->asUint() <= UINT32_MAX;
    case ValueType::Real:
    {
        double rv = mImpl->asReal();
        return (rv >= 0) && (rv <= UINT32_MAX) && IsIntegral(rv);
    }
    default:
        break;
    }

    return false;
}

bool Value::isInt64() const
{
    switch (type()) 
    {
    case ValueType::Int:
        return true;
    case ValueType::Uint:
        return mImpl->asUint() <= uint64_t(INT64_MAX);
    case ValueType::Real:
        // Note that max int64_t (= 2^63 - 1) is not exactly representable as a
        // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
        // require the value to be strictly less than the limit.
    {
        double rv = mImpl->asReal();  
        return (rv >= double(INT64_MIN)) && 
               (rv < double(INT64_MAX)) && 
                IsIntegral(rv);
    }
    default:
        break;
    }

    return false;
}

bool Value::isUInt64() const
{
    switch (type()) 
    {
    case ValueType::Int:
        return mImpl->asInt() >= 0;
    case ValueType::Uint:
        return true;
    case ValueType::Real:
        // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
        // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
        // require the value to be strictly less than the limit.
    {
        double rv = mImpl->asReal();
        return (rv >= 0) && (rv< gMaxUInt64AsDouble) && IsIntegral(rv);
    }

    default:
        break;
    }

    return false;
}

bool Value::isIntegral() const
{
    switch (type()) 
    {
    case ValueType::Int:
    case ValueType::Uint:
        return true;
    case ValueType::Real:
        // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
        // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
        // require the value to be strictly less than the limit.
    {
        double rv = mImpl->asReal();
        return (rv >= double(INT64_MIN)) && 
               (rv < gMaxUInt64AsDouble) && IsIntegral(rv); 
    }

    default:
        break;
    }

    return false;
}

bool Value::isDouble() const
{
    return (type() == ValueType::Int) || 
           (type() == ValueType::Uint) || 
           (type() == ValueType::Real);
}

bool Value::isNumeric() const
{
    return isDouble();
}

bool Value::isString() const
{
    return type() == ValueType::String;
}

bool Value::isArray() const
{
    return type() == ValueType::Array;
}

bool Value::isObject() const
{
    return type() == ValueType::Object;
}

ValueType Value::type() const
{
    return mImpl->type();
}

/** get value(payload) */
int32_t Value::asInt() const
{
    switch (type()) 
    {
    case ValueType::Int:
        JSON_ASSERT_MESSAGE(isInt(), "Int out of Int range");
        return int32_t(mImpl->asInt());
    case ValueType::Uint:
        JSON_ASSERT_MESSAGE(isInt(), "Uint out of Int range");
        return int32_t(mImpl->asUint());
    case ValueType::Real:
        JSON_ASSERT_MESSAGE(InRange(mImpl->asReal(), INT32_MIN, INT32_MAX),
            "double out of Int range");
        return int32_t(mImpl->asReal());
    case ValueType::Null:
        return 0;
    case ValueType::Boolean:
        return mImpl->asBool() ? 1 : 0;
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to Int.");
}

uint32_t Value::asUInt() const
{
    switch (type()) 
    {
    case ValueType::Int:
        JSON_ASSERT_MESSAGE(isUInt(), "Int out of UInt range");
        return uint32_t(mImpl->asInt());
    case ValueType::Uint:
        JSON_ASSERT_MESSAGE(isUInt(), "Uint out of UInt range");
        return uint32_t(mImpl->asUint());
    case ValueType::Real:
        JSON_ASSERT_MESSAGE(InRange(mImpl->asReal(), 0, UINT32_MAX),
            "double out of UInt range");
        return uint32_t(mImpl->asReal());
    case ValueType::Null:
        return 0;
    case ValueType::Boolean:
        return mImpl->asBool() ? 1 : 0;
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
}

int64_t Value::asInt64() const
{
    switch (type()) 
    {
    case ValueType::Int:
        return mImpl->asInt();
    case ValueType::Uint:
        JSON_ASSERT_MESSAGE(isInt64(), "Uint out of Int64 range");
        return int64_t(mImpl->asUint());
    case ValueType::Real:
        JSON_ASSERT_MESSAGE(InRange(mImpl->asReal(), INT64_MIN, INT64_MAX),
            "double out of Int64 range");
        return int64_t(mImpl->asReal());
    case ValueType::Null:
        return 0;
    case ValueType::Boolean:
        return mImpl->asBool() ? 1 : 0;
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
}

uint64_t Value::asUInt64() const
{
    switch (type()) 
    {
    case ValueType::Int:
        JSON_ASSERT_MESSAGE(isUInt64(), "Int out of UInt64 range");
        return mImpl->asUint();
    case ValueType::Uint:
        return mImpl->asUint();
    case ValueType::Real:
        JSON_ASSERT_MESSAGE(InRange(mImpl->asReal(), 0, UINT64_MAX),
            "double out of UInt64 range");
        return uint64_t(mImpl->asReal());
    case ValueType::Null:
        return 0;
    case ValueType::Boolean:
        return mImpl->asBool() ? 1 : 0;
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
}

bool Value::asBool() const
{
    switch (type()) {
    case ValueType::Boolean:
        return mImpl->asBool();
    case ValueType::Null:
        return false;
    case ValueType::Int:
        return mImpl->asInt() ? true : false;
    case ValueType::Uint:
        return mImpl->asUint() ? true : false;
    case ValueType::Real: {
        // According to JavaScript language zero or NaN is regarded as false
        const auto value_classification = std::fpclassify(mImpl->asReal());
        return value_classification != FP_ZERO && value_classification != FP_NAN;
    }
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to bool.");
}

float Value::asFloat() const
{
    switch (type()) 
    {
    case ValueType::Int:
        return static_cast<float>(mImpl->asInt());
    case ValueType::Uint:
        // This can fail (silently?) if the value is bigger than MAX_FLOAT.
        return static_cast<float>(integerToDouble(mImpl->asUint()));
    case ValueType::Real:
        return static_cast<float>(mImpl->asReal());
    case ValueType::Null:
        return 0.0;
    case ValueType::Boolean:
        return mImpl->asBool() ? 1.0f : 0.0f;
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to float.");
}

double Value::asDouble() const
{
    switch (type()) 
    {
    case ValueType::Int:
        return static_cast<double>(mImpl->asInt());
    case ValueType::Uint:
        return integerToDouble(mImpl->asUint()); 
    case ValueType::Real:
        return mImpl->asReal();
    case ValueType::Null:
        return 0.0;
    case ValueType::Boolean:
        return mImpl->asReal() ? 1.0 : 0.0;
    default:
        break;
    }
    JSON_FAIL_MESSAGE("Value is not convertible to double.");
}

std::string Value::asString() const
{
    switch (type()) 
    {
    case ValueType::Null:
        return "";
    case ValueType::String: 
    {
        if (mImpl->asStr())
        {
            /** todo refine me, decode/encode to utf8?? */
            return std::string(mImpl->asStr());           
        }

        return "";
    }
    case ValueType::Boolean:
        return mImpl->asBool() ? "true" : "false";
    case ValueType::Int:
        return ValueToString(mImpl->asInt());
    case ValueType::Uint:
        return ValueToString(mImpl->asUint());
    case ValueType::Real:
        return ValueToString(mImpl->asReal());
    default:
        JSON_FAIL_MESSAGE("Type is not convertible to string");
    }
}

const char* Value::asCString() const
{
    JSON_ASSERT_MESSAGE(type() == ValueType::String,
        "in Json::Value::asCString(): requires ValueType::String");

    /** todo refine me, decode/encode to utf8? */
    return mImpl->asStr();
}

/** array or obejct operation */
    /** Number of values in array or object */
ArrayIndex Value::size() const
{
    switch (type()) 
    {
    case ValueType::Null:
    case ValueType::Int:
    case ValueType::Uint:
    case ValueType::Real:
    case ValueType::Boolean:
    case ValueType::String:
        return 0;
    case ValueType::Array: // size of the array is highest index + 1
        return ArrayIndex(mImpl->asArray().size());
    case ValueType::Object:
        return ArrayIndex(mImpl->asObject().size());
    default:
        break;
    }

    VALUE_ASSERT_UNREACHABLE;
    return 0; // unreachable;
}


/** \brief Return true if empty array, empty object, or null;
    *         otherwise, false. 
    */
bool Value::empty() const
{
    if (isNull() || isArray() || isObject())
    {
         return size() == 0u;
    }        
    
    return false;
}


/** Return !isNull() */
Value::operator bool() const
{
    return !isNull();
}


/** Remove all object members and array elements.
    * \pre type() is array, object, or null
    * \post type() is unchanged
    */
void Value::clear()
{
    JSON_ASSERT_MESSAGE((type() == ValueType::Null) || 
                        (type() == ValueType::Array) ||
                        (type() == ValueType::Object),
                        "in Json::Value::clear(): requires complex value");

    switch (type()) 
    {
    case ValueType::Array:
        mImpl->asArray().clear();
        break;
    case ValueType::Object:
        mImpl->asObject().clear();
        break;
    default:
        break;
    }
}

/** Access an array element (zero based index).
 * If the array contains less than index element, then throw exception
 * in the array so other its size is index+1.
 * \pre isArray() == true
 * \param index Zero-based index of element.
 */
Value& Value::operator[](ArrayIndex index)
{
    JSON_ASSERT_MESSAGE((type() == ValueType::Array),
        "in Json::Value::operator[](ArrayIndex): requires ValueType::Array");

    if (index < mImpl->asArray().size())
    {
        return mImpl->asArray()[index];
    }

    JSON_FAIL_MESSAGE("index outof range of array size");
}

/** Access an array element (zero based index).
 * If the array contains less than index element, then throw exception
 * in the array so other its size is index+1.
 * \pre isArray() == true
 * \param index Zero-based index of element.
 */
const Value& Value::operator[](ArrayIndex index) const
{
    JSON_ASSERT_MESSAGE((type() == ValueType::Array),
        "in Json::Value::operator[](ArrayIndex): requires ValueType::Array");

    if (index < mImpl->asArray().size())
    {
        return mImpl->asArray()[index];
    }

    JSON_FAIL_MESSAGE("index outof range of array size");
}

/* If the array contains at least index+1 elements, 
 *  returns the element value, otherwise returns defaultValue.
 *  
 */ 
Value Value::get(ArrayIndex index, const Value& defaultValue) const
{
    JSON_ASSERT_MESSAGE((type() == ValueType::Array),
        "in Json::Value::operator[](ArrayIndex): requires ValueType::Array");

    if (index < mImpl->asArray().size())
    {
        return mImpl->asArray()[index];
    }

    return defaultValue;
}
    
/* Return true if index < size(). */
bool Value::isValidIndex(ArrayIndex index) const
{
    return index < size();
}

/* \brief Append a Value at the end of the array. */
Value& Value::append(const Value& value)
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Array),
        "in append(value): requires ValueType::Array or ValueType::Null");

    if (type() == ValueType::Null)
    {
        * this = Value(ValueType::Array);
    }

    mImpl->asArray().push_back(value);

    return mImpl->asArray().back();
}

Value& Value::append(Value&& value)
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Array),
        "in append(value): requires ValueType::Array or ValueType::Null");

    if (type() == ValueType::Null)
    {
        *this = Value(ValueType::Array);
    }

    mImpl->asArray().push_back(std::move(value));

    return mImpl->asArray().back();
}

/* Access an object value by name, create a null member if it does not exist.
 * \param key may contain embedded nulls.
 */
Value& Value::operator[](const std::string& key)
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Object),
        "in operator[](key): requires ValueType::Object or ValueType::Null");

    if (type() == ValueType::Null)
    {
        *this = Value(ValueType::Object);
    }

    return mImpl->asObject()[key];

}
    
/* Access an object value by name, returns null if there is no member with
 * other name.
 * \param key may contain embedded nulls.
 */
const Value& Value::operator[](const std::string& key) const
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Object),
        "in operator[](key): requires ValueType::Object or ValueType::Null");

    if (type() != ValueType::Null)
    {
        ObjectValue& ov = mImpl->asObject();  
        ObjectValue::iterator it = ov.find(key);

        if (it != ov.end())
        {
            return it->second;
        }
    }

    return nullSingleton();
}

/* Return the member named key if it exist, defaultValue otherwise.
 * \note deep copy
 * \param key may contain embedded nulls.
 */
Value Value::get(const std::string& key, const Value& defaultValue) const
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Object),
        "in get(key, defaultVal): requires ValueType::Object or ValueType::Null");

    if (type() != ValueType::Null)
    {
        ObjectValue& ov = mImpl->asObject();
        ObjectValue::iterator it = ov.find(key);

        if (it != ov.end())
        {
            return it->second;
        }
    }

    return defaultValue;
}

/** object member or array element operation */
/* \pre type() is object or null
 * \post type() is unchanged
 */
void Value::removeMember(const std::string& key)
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Object),
        "in removeMember(key): requires ValueType::Object");
    if (type() != ValueType::Null)
    {
        mImpl->asObject().erase(key);
    }
}

/** \brief Remove the indexed array element.
 *  O(n) expensive operations.
 * \return true if removed (no exceptions)
 */
bool Value::removeIndex(ArrayIndex index)
{
    bool ret = false;
    if (type() == ValueType::Array)
    {
        ArrayValue& av = mImpl->asArray();

        if (index < av.size())
        {
            av.erase(av.begin() + index);
            ret = true;
        }
    }
    
    return ret;
}

/* Return true if the object has a member named key.
 * \param key may contain embedded nulls.
 */
bool Value::isMember(const std::string& key) const
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Object),
        "in isMember(key): requires ValueType::Object or ValueType::Null");

    if (type() == ValueType::Object)
    {
        return (mImpl->asObject().find(key) != mImpl->asObject().end());
    }

    return false;
}

/* \brief Return a list of the member names.
 *
 * If null, return an empty list.
 * \pre type() is object or null
 * \post if type() was null, it remains null
 */
std::vector<std::string> Value::getMembers()
{
    JSON_ASSERT_MESSAGE(
        (type() == ValueType::Null) || (type() == ValueType::Object),
        "in getMemberNames(), value must be ValueType::Object");

    std::vector<std::string> members;

    if (type() == ValueType::Object)
    {
        ObjectValue& ov = mImpl->asObject();
        ObjectValue::iterator it = ov.begin();
        for (; it != ov.end(); it++)
        {
            members.push_back(it->first);
        }
    }

    return std::move(members);
}


/* \brief Return all members of an object .
*
* \pre type() is object
* If type() not object, then thow exception
*/
const Value::ObjectMembers& Value::getObjectMembers() const
{
    JSON_ASSERT_MESSAGE((type() == ValueType::Object),
        "in getObjectMembers(), value must be ValueType::Object");
    return mImpl->asObject();
}

/* \brief Return all element of an array .
 *
 * \pre type() is array
 * If type() not array, then thow exception
 */
const Value::ArrayElements& Value::getArrayElements() const
{
    JSON_ASSERT_MESSAGE((type() == ValueType::Array),
        "in getArrayElements(), value must be ValueType::Array");
    return mImpl->asArray();
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

/// used internally
CORE_NO_RETURN void ThrowRuntimeError(const std::string& msg)
{
    throw std::runtime_error(msg.c_str());
}

/// used internally
CORE_NO_RETURN void ThrowLogicError(const std::string& msg)
{
    throw std::logic_error(msg.c_str());
}

} /** namespace core */
} /** namespace collie */
